package sample;

import sample.tree.*;
import sample.tree.node.*;
import sample.tree.node.expr.*;
import sample.tree.visitor.*;

// Simple demo of JASTgen.
public class Main {

	public static void main(String[] args) {

		Context c = new Context();

		Expr v1 = c.getConst(1);  // 1
		Expr v2 = c.getConst(2);  // 2
		Expr v3 = c.getConst(3);  // 3
		Expr v5 = c.getConst(5);  // 5
		Expr v9 = c.getConst(9);  // 9

		Expr x = c.getAdd(v5, v1);  // x = 5 + 1
		Expr y = c.getSub(v9, v3);  // y = 9 - 3
		Expr z = c.getMul(v2, v3);  // z = 2 * 3
		Expr w = c.getDiv(x, y);  // w = x / y
		Expr r = c.getAdd(z, w);  // r = z + w (root)

		Printer out = new Printer();
		r.visit(out);  // print
		System.out.println();

		Calc calc = new Calc(c);
		Expr r_ = r.visit(calc);  // optimization
		r_.visit(out);  // print
		System.out.println();
	}
}

// Output tree demo.
class Printer implements VoidVisitor {

	@Override
	public void inAdd(Add node) {
		printBin(node.getLhs(), node.getRhs(), "+");
	}

	@Override
	public void inSub(Sub node) {
		printBin(node.getLhs(), node.getRhs(), "-");
	}

	@Override
	public void inMul(Mul node) {
		printBin(node.getLhs(), node.getRhs(), "*");
	}

	@Override
	public void inDiv(Div node) {
		printBin(node.getLhs(), node.getRhs(), "/");
	}

	@Override
	public void inConst(Const node) {
		System.out.print(node.getValue());
	}

	private void printBin(Expr lhs, Expr rhs, String op) {
		System.out.print("(");
		lhs.visit(this);
		System.out.print(" " + op + " ");
		rhs.visit(this);
		System.out.print(")");
	}
}

// Optimization (folding) demo.
class Calc extends NodeReplacer {

	public Calc(Context context) {
		super(context);
	}

	@Override
	protected Expr replaceAdd(
			final Context c,
			final Expr lhs,
			final Expr rhs)
	{
		if((lhs instanceof Const) && (rhs instanceof Const)) {
			Const lhs_ = (Const)lhs;
			Const rhs_ = (Const)rhs;
			return c.getConst(lhs_.getValue() + rhs_.getValue());
		}
		return super.replaceAdd(c, lhs, rhs);  // Do nothing.
	}

	@Override
	protected Expr replaceSub(
			final Context c,
			final Expr lhs,
			final Expr rhs)
	{
		if((lhs instanceof Const) && (rhs instanceof Const)) {
			Const lhs_ = (Const)lhs;
			Const rhs_ = (Const)rhs;
			return c.getConst(lhs_.getValue() - rhs_.getValue());
		}
		return super.replaceAdd(c, lhs, rhs);  // Do nothing.
	}

	@Override
	protected Expr replaceMul(
			final Context c,
			final Expr lhs,
			final Expr rhs)
	{
		if((lhs instanceof Const) && (rhs instanceof Const)) {
			Const lhs_ = (Const)lhs;
			Const rhs_ = (Const)rhs;
			return c.getConst(lhs_.getValue() * rhs_.getValue());
		}
		return super.replaceAdd(c, lhs, rhs);  // Do nothing.
	}

	@Override
	protected Expr replaceDiv(
			final Context c,
			final Expr lhs,
			final Expr rhs)
	{
		if((lhs instanceof Const) && (rhs instanceof Const)) {
			Const lhs_ = (Const)lhs;
			Const rhs_ = (Const)rhs;
			return c.getConst(lhs_.getValue() / rhs_.getValue());
		}
		return super.replaceAdd(c, lhs, rhs);  // Do nothing.
	}
}
